package com.oj.controller;

import com.oj.config.OAuthConfig;
import com.oj.config.OJConfig;
import com.oj.entity.*;
import com.oj.service.impl.I18nService;
import com.oj.util.*;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.ui.Model;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.*;

@Slf4j
@Controller
@RequestMapping
public class IndexController {
    @Autowired
    private OJConfig ojConfig;

    @Autowired
    FreeMarkerConfigurer freeMarkerConfigurer;

    @Autowired
    private I18nService i18nService;

    @GetMapping("/robots.txt")
    public ResponseEntity<Resource> robots(HttpServletRequest request) {
        try {
            log.info("robots.txt - "+ request.getHeader("User-Agent"));
            ApplicationHome h = new ApplicationHome(getClass());
            if (h == null || h.getSource() == null || h.getSource().getParentFile() == null) {
                return null;
            }
            String p_ = h.getSource().getParentFile().toString()+"/data/robots.txt";
            Path path = Paths.get(p_);
            Resource resource = new UrlResource(path.toUri());
            if (resource.exists() || resource.isReadable()) {
                return ResponseEntity
                        .ok()
                        .contentType(MediaType.TEXT_PLAIN)
                        .body(resource);
            } else {
            }
        } catch (Exception e) {
        }
        return null;
    }

    @RequestMapping(value = {"", "/", "/home"}, method = RequestMethod.GET)
    public ModelAndView toIndex(){
        ApplicationHome h = new ApplicationHome(getClass());
        File jarF = h.getSource();
        String appPath = jarF.getParentFile().toString();
        String directory = appPath + File.separator + "upload" + File.separator + "maker";
        File uploadFileSaveDir = new File(directory);
        if(!uploadFileSaveDir.exists()){
            uploadFileSaveDir.mkdirs();
        }
        String filePath = directory + File.separator + "home-page.md";
        ModelAndView mv = new ModelAndView();
        mv.addObject("content", StreamHandler.readEx(filePath));
        mv.setViewName("index.html");
        return mv;
    }

    @GetMapping(value = {"/enter"})
    public ModelAndView toLogin(){
        ModelAndView mv=new ModelAndView();
        mv.addObject("githubClientID", OAuthConfig.getGithubClientID());
        mv.addObject("giteeClientID", OAuthConfig.getGiteeClientID());
        mv.addObject("giteeRedrectUrl", OAuthConfig.getGiteeRedrectUrl());
        mv.addObject("googleClientID", OAuthConfig.getGoogleClientID());
        mv.addObject("googleRedrectUrl", OAuthConfig.getGoogleRedrectUrl());
        mv.setViewName("user/login.html");
        return mv;
    }

    @RequestMapping(value = {"/admin"}, method = RequestMethod.GET)
    public String toAdminIndex(Model model){
        return "admin/index";
    }

    @RequestMapping(value = {"/telnet"}, method = RequestMethod.GET)
    public ModelAndView totelnet(HttpServletRequest request) {
        ModelAndView mv=new ModelAndView();
        mv.addObject("proxyAddress", request.getServerName()+":"+request.getServerPort());
        mv.setViewName("admin/telnet.html");
        return mv;
    }

    @RequestMapping(value = {"/admin/kernel"}, method = RequestMethod.GET)
    public ModelAndView toKernelCfg(Model model){
        ModelAndView mv=new ModelAndView();
        mv.addObject("config", StreamHandler.readEx(ojConfig.getConfigFile()));
        mv.setViewName("admin/kernel.html");
        return mv;
    }

    @ResponseBody
    @RequestMapping(value = {"/api/admin/get-logs"})
    public Result getLogs(@RequestBody OJFileReader ojFileReader,
                          HttpServletRequest request, HttpServletResponse response, Model model){
        try {
            OJFileReader reader =
                    StreamHandler.readLineFile("logs"+ File.separator + ojFileReader.getFileName(),
                            ojFileReader.getFromLineNum(), ojFileReader.getReadLineNum(), false);
            return Result.success(reader);
        } catch (Exception e) {
            return Result.error(CodeMsg.INNER_FAULT.locale());
        }
    }

    @ResponseBody
    @RequestMapping(value = {"/api/admin/get-kernel-config"}, method = RequestMethod.GET)
    public Result getKernelCfg(Model model){
        log.info("get kernel config file:" + ojConfig.getConfigFile());
        String content = StreamHandler.readEx(ojConfig.getConfigFile());
        log.info(content);
        return Result.success(content);
    }

    @ResponseBody
    @PostMapping(value = "/api/admin/save-kernel-config")
    public Result saveKernelCfg(@RequestParam Map<String, Object> map, HttpServletRequest request){
        if (map.get("content").toString().length() == 0) {
            return Result.error(CodeMsg.INPUT_CONTENT_NULL.locale());
        }
        if (!StreamHandler.write(ojConfig.getConfigFile(), map.get("content").toString())) {
            return Result.error(CodeMsg.INNER_FAULT.locale());
        }
        log.info("save kernel config:");
        log.info(map.get("content").toString());
        return Result.success();
    }

    @ResponseBody
    @PostMapping(value = "/api/admin/homemaker")
    public Result postHomemaker(@RequestParam Map<String, Object> map, HttpServletRequest request){
        //log.info(map.toString());
        //log.info(map.toString().getBytes(StandardCharsets.UTF_8).toString());
        if (map.get("contentHome") != null) {
            maker("home-page.md", map.get("contentHome").toString());
        }
        if (map.get("contentFooter") != null) {
            maker("footer-desc.md", map.get("contentFooter").toString());
            try {
                Map map_ = new HashMap();
                map_.put("footer", map.get("contentFooter").toString());
                Template template = freeMarkerConfigurer.getConfiguration().getTemplate("freemarker/footer.ftl");
                MyFreeMarker.generator(template, "footer.html",map_);
            } catch (IOException | TemplateException e) {
                throw new RuntimeException(e);
            }
        }
        if (map.get("contentSidebar") != null) {
            maker("sidebar-top.md", map.get("contentSidebar").toString());
        }
        return Result.success();
    }

    public void makerTemplate(String fileName, String content) {
        String directory = new String();
        try {
            if (ResourceUtils.getURL("classpath:").getPath().startsWith("file:")) {
                directory = System.getProperty("user.dir") + "/templates/freemarker/";
            } else {
                directory = ResourceUtils.getURL("classpath:").getPath() + "/templates/freemarker/";
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
        File fileSaveDir = new File(directory);
        if(!fileSaveDir.exists()){
            fileSaveDir.mkdirs();
        }
        String filePath = directory + File.separator + fileName;
        try {
            StreamHandler.write(filePath, content);
            log.info("makerTemplate success : " + filePath);
            return;
        } catch (Exception e) {
            return;
        }
    }
    public void maker(String fileName, String content) {
        ApplicationHome h = new ApplicationHome(getClass());
        File jarF = h.getSource();
        String appPath = jarF.getParentFile().toString();
        String directory = appPath + File.separator + "upload" + File.separator + "maker";
        File uploadFileSaveDir = new File(directory);
        if(!uploadFileSaveDir.exists()){
            uploadFileSaveDir.mkdirs();
        }
        String filePath = directory + File.separator + fileName;
        try {
            StreamHandler.write(filePath, content);
            log.info("Homemaker success : " + filePath);
            return;
        } catch (Exception e) {
            return;
        }
    }

    @ResponseBody
    @RequestMapping(value = {"/api/admin/dir"})
    public Result dir(@RequestBody Directory directory, HttpServletRequest request) {
        //log.info(directory.toString());
        String basePath = "";
        if ("OJ".equals(directory.getType())) {
            basePath = ojConfig.getOjPath();
        } else if ("LOGS".equals(directory.getType())) {
            ApplicationHome h = new ApplicationHome(getClass());
            String appPath = h.getSource().getParentFile().toString();
            basePath = appPath + File.separator + "logs";
        }
        //log.info("basePath:" + basePath);
        String subPath = "";
        String fullPath = "";
        if (directory.getCurrent() != null && directory.getCurrent().length() != 0) {
            subPath = directory.getCurrent();
        }

        fullPath = Paths.get(basePath + File.separator + subPath).normalize().toString();
        if (!fullPath.startsWith(basePath)) {
            return Result.error(CodeMsg.PARAM_INVALID.locale());
        }
        //log.info("fullPath:" + fullPath);
        subPath = fullPath.substring(basePath.length());
        Directory directory_ = new Directory();
        directory_.setParent(directory.getCurrent());
        directory_.setCurrent(subPath + File.separator);
        directory_.setFiles(new ArrayList<>());
        String[] list = new File(fullPath).list();
        if (list != null) {
            for (int i = 0; i < list.length; i++) {
                String path = fullPath + File.separator + list[i];
                File f = new File(path);
                OJFile ojFile = new OJFile();
                if (f.isDirectory()) {
                    ojFile.setName(list[i] + File.separator);
                } else {
                    ojFile.setName(list[i]);
                }

                ojFile.setLength(f.length());
                ojFile.setLastModified(new Date(f.lastModified()));
                ojFile.setIsDirectory(f.isDirectory()?1:0);
                directory_.getFiles().add(ojFile);
                //log.info(ojFile.toString());
            }
        }

        Collections.sort(directory_.getFiles().subList(0, directory_.getFiles().size()), new Comparator<OJFile>() {
            public int compare(OJFile r1, OJFile r2) {
                long diff = r2.getLastModified().getTime() - r1.getLastModified().getTime();
                if (diff > 0) {
                    return 1;
                }else if (diff < 0) {
                    return -1;
                }
                return 0;
            }
        });
        //log.info(directory_.toString());
        return Result.success(directory_);
    }
    @ResponseBody
    @RequestMapping(value = {"/api/admin/get-oj-file"})
    public Result getOjFile(@RequestBody OJFileReader ojFileReader,
                          HttpServletRequest request, HttpServletResponse response, Model model){
        try {
            //log.info(ojFileReader.toString());
            String basePath = "";
            if ("OJ".equals(ojFileReader.getType())) {
                basePath = ojConfig.getOjPath();
            } else if ("LOGS".equals(ojFileReader.getType())) {
                ApplicationHome h = new ApplicationHome(getClass());
                String appPath = h.getSource().getParentFile().toString();
                basePath = appPath + File.separator + "logs";
            }
            OJFileReader reader =
                    StreamHandler.readLineFile(
                            Paths.get(basePath +
                                    File.separator +
                                    ojFileReader.getFileName()).normalize().toString(),
                            ojFileReader.getFromLineNum(), ojFileReader.getReadLineNum(), false);
            //log.info(reader.toString());
            return Result.success(reader);
        } catch (Exception e) {
            return Result.error(CodeMsg.INNER_FAULT.locale());
        }
    }
    @RequestMapping(value = {"/tools"}, method = RequestMethod.GET)
    public String toTools(Model model){
        return "tools/json";
    }

    @RequestMapping(value = {"/search"}, method = RequestMethod.GET)
    public ModelAndView search(String q){
        ModelAndView mv=new ModelAndView();
        mv.addObject("q", q);
        mv.setViewName("search.html");
        return mv;
    }

    @ResponseBody
    @RequestMapping(value = {"/api/i18n/get"})
    public Result apiI18n(String message, HttpServletRequest request){
        try {
            String ret = i18nService.getMessage(message);
            return Result.success(ret);
        } catch (Exception e) {
            return Result.error(CodeMsg.INNER_FAULT.locale());
        }
    }
}